import struct,os, sys, time, socket, random,threading
from SocketServer import ThreadingTCPServer, StreamRequestHandler

PORT=8989
LOG_PATH='dummyPMS.log'
REGION_MAX = 17
AGE_MAX = 7
GENDER_MAX = 2

MSG_DELIMITER = '%'
MSG_END = '\r\n\0'
MSG_T={}
MSG_T['HBREQ']='HBREQ%%%d'
MSG_T['PERE']='PERE'
MSG_T['STRE']='STRE'
MSG_T['ANNO']='ANNO%%%s%%%s'
 

def fileLog(msg):
	print msg
	fp = open(LOG_PATH,'a')
	data = '[%s] %s\n' % (time.strftime('%Y-%m-%d %H:%M:%S'),msg)
	fp.write(data)
	fp.close()
	
def execCMD(cmd,args):
	if os.fork() == 0:
		os.execv(cmd,args)
	
class HostAgent:
	def __init__(self,gsID,path,start,ip,port,config):
		self.m_gsID = gsID
		self.m_exe_path = path
		self.m_start = start
		self.m_ip = ip
		self.m_port = port
		self.m_config = config
		self.m_status = 'NONE'
		self.m_conn = None
		self.m_gs_info = 'NO INFO'	
	
	def resInit(self,processID,ip,port,conn):
		self.m_processID = processID
		self.m_gs_info = '%s:%s' % (ip,port)
		self.m_conn = conn
		self.m_hbseq = 0
		self.m_lastHB = 0
		self.m_perf = [0,0,0,0,0,0,0,0,0,0]
		self.m_curUser = '0'
		self.m_stat = []
		self.m_pcbCurUser = '0'		
		self.m_pcb_stat = []
		self.initStatistic()
		self.m_status = 'INIT'
		
	def initStatistic(self):
		for x in range(0,REGION_MAX):
			ageL = []
			for y in range(0,AGE_MAX):
				genderL = []
				for z in range(0,GENDER_MAX):
					genderL.append(0)
				ageL.append(genderL)
			self.m_stat.append(ageL)
			self.m_pcb_stat.append(ageL)
#		print self.m_stat

	def gsStart(self):
		temp = '%s %s /GSID:%s /HAIP:%s /HAPORT:%s /CNFGFILE:%s' % (self.m_exe_path,self.m_start,self.m_gsID,self.m_ip,self.m_port,self.m_config)
		args = temp.split(' ')
		execCMD(self.m_exe_path,args)
		return True
			
	def gsStop(self):
#		cmd = 'kill'
#		args = ['-9', '%s' % (self.m_processID)]
#		execCMD(cmd,args)
#		temp = '%s stop /GSID:%s /HAIP:%s /HAPORT:%s /CNFGFILE:%s' % (self.m_exe_path,self.m_gsID,self.m_ip,self.m_port,self.m_config)
#		args = temp.split(' ')
#		execCMD(self.m_exe_path,args)

		if None != self.m_processID:
			os.kill(int(self.m_processID),9)
		else:
			fileLog('[gsStop] HA is not started')
		return True
	
	def gsTest(self):
		if self.m_status == 'NONE' or self.m_status == 'DOWN' :
			return
		msg = self.heartBeat()
		self.sendReq(msg)
		time.sleep(1)

		msg = self.perfomance()
		self.sendReq(msg)
		time.sleep(1)

		msg = self.statistic()
		self.sendReq(msg)
		time.sleep(1)
		
#		msg = self.announce(notiMsg)
#		self.sendReq(msg)
		
			
	def heartBeat(self):
		self.m_hbseq += 1
		self.m_status = 'HBREQ'
		return MSG_T['HBREQ'] % (self.m_hbseq)
		
	def perfomance(self):
		return MSG_T['PERE']
		
	def statistic(self):
		return MSG_T['STRE']
	
	def announce(self,msg):
#		return MSG_T['ANNO'] % (msg,time.strftime('%Y-%m-%d-%H-%M-%S'))
		self.sendReq(MSG_T['ANNO'] % (msg,time.strftime('%Y-%m-%d-%H-%M-%S')))
		
	def resHeartBeat(self,gsid,hbseq):
		if gsid == self.m_gsID and hbseq == self.m_hbseq:
			fileLog('[resHearBeat] %s is alive' % (self.m_gsID))
			self.m_status = 'HBANS'
			self.m_lastHB = time.time()
		else:
			fileLog('[resHearBeat] %s wrong param' % (self.m_gsID))
	
	def resPerfomance(self,gsid,countL):
		print '[resPerfomance]',countL
		i = 0
		for data in countL:
			self.m_perf[i]=int(data)
			i += 1
		fileLog('[resPerfomance] %s perf:%s' % (self.m_gsID,','.join(countL)))
			
	def resStatistic(self,curUser,statL):
		print '[resStatistic]',curUser,statL	
		self.m_curUser = curUser
		i = 0
		for x in range(0,REGION_MAX):
			for y in range(0,AGE_MAX):
				for z in range(0,GENDER_MAX):
					self.m_stat[x][y][z] = int(statL[i])
					i += 1

	def resStatisticPcb(self,curUser,statL):
		self.m_pcbCurUser = curUser
		i = 0
		for x in range(0,REGION_MAX):
			for y in range(0,AGE_MAX):
				for z in range(0,GENDER_MAX):
					self.m_pcb_stat[x][y][z] = int(body[i])
					i += 1

	def sendReq(self,msg):
		self.m_conn.send(msg + MSG_END)
		
	def printInfo(self):
		#GSID,STATUS,IP,PORT,PATH,START,CNF,GSINFO
		return '%s,%s,%s,%s,%s,%s,%s,%s' % (self.m_gsID,self.m_status,self.m_ip,self.m_port,self.m_exe_path,self.m_start,self.m_config,self.m_gs_info)
	
	def printPerf(self):
		#COUNT1 ~ COUNT10
		countL = []
		for perf in self.m_perf:
			countL.append(str(perf))
		return ','.join(countL)
		
	def printStat(self):
		statL=[]
		statL.append(self.m_curUser)
		for x in range(0,REGION_MAX):
			for y in range(0,AGE_MAX):
				for z in range(0,GENDER_MAX):
					#area,age,gender,stat				
					statVal = '%d,%d,%d,%d' % (x,y,z,self.m_pcb_stat[x][y][z])
					statL.append(statVal)
		return '|'.join(statL)
					
class PMSManager:
	def __init__(self):
		self.m_hostAgents = {}
	
	def getHA(self,gsID):
		return self.m_hostAgents.get(gsID)
		
	def listHA(self):
		haL = []
		haL.append('GSID,STATUS,IP,PORT,PATH,START,CNF,GSINFO')
		for key in self.m_hostAgents.keys():
			haL.append(self.m_hostAgents[key].printInfo())
		return '|'.join(haL)
	
	def addHA(self,gsID,path,start,ip,port,config):
		if None == self.m_hostAgents.get(gsID):
			ha = HostAgent(gsID,path,start,ip,port,config)
			self.m_hostAgents[gsID] = ha
		else:
			fileLog('[addHA] GS[%s] already exist' % (gsID) )
			
	def removeHA(self,gsID):
		if None != self.m_hostAgents.get(gsID):
			fileLog('[removeHA] gsID:%s is removed' % (gsID))
			self.m_hostAgents.pop(gsID)
			
	def findGSIDfromConn(self,conn):
		for key, value in self.m_hostAgents.items():
			if value.m_conn == conn:
				return key
		return ""
	
	def setStatus(self,gsID,status):
		ha = self.m_hostAgents.get(gsID)
		if None != ha:
			print '[setStatus] gsID:%s status[%s]' % (gsID,status)
			ha.m_status = status

class RequestHandler(StreamRequestHandler):
	m_pmsManager = 	PMSManager()	
	def handle(self):
		print '[RequestHandler]connection from',self.client_address,self.request
		try:
			conn = self.request
			while 1:
				buf = conn.recv(1024)
				if buf == None :
					print '[RequestHandler] client is disconnected',self.client_address,self.request
					return None
				fileLog( "[RequestHandler] GS[%s] buf:%s" % (self.m_pmsManager.findGSIDfromConn(self.request),buf)	)	
				resMsg = self.response(buf.rstrip(MSG_END))
				if resMsg != None:
					fileLog( '[RequestHandler] resMsg:%s' % (resMsg))
					conn.send(resMsg)
		except socket.error:
			print '[RequestHandler]SocketError',self.request
			self.m_pmsManager.setStatus(self.m_pmsManager.findGSIDfromConn(self.request),'DOWN')
		except Exception,msg:
			print '[RequestHandler] exception!!',msg
			return
			
	def response(self, bodyMsg):
		body = bodyMsg.split(MSG_DELIMITER)
		if body[0] == 'INIT': #gsID,processID,gsIP,gsPort
			if len(body) >= 5:
				ha = self.m_pmsManager.getHA(body[1])
				if None != ha:
					ha.resInit(body[2],body[3],body[4],self.request)
				else:
					fileLog( '[response] Fail to INIT : Not found HA(GSID:%s)' % (body[1]))
			return None
		if body[0] == 'INFA': #gsID,errCode
			if len(body) >= 3:
				ha = self.m_pmsManager.getHA(body[1])
				if None != ha:
					ha.m_status = body[0]
					ha.m_gs_info = 'Error Code:%s' % (body[2])
				else:
					fileLog( '[response] Fail to INIT : Not found HA(GSID:%s)' % (body[1]))
			return None			
		elif body[0] == 'HBANS':
			if len(body) >= 2:
				ha = self.m_pmsManager.getHA(body[1])
				if None != ha:
					ha.resHeartBeat(body[1],int(body[2]))
			return None
		elif body[0] == 'PEAN':
			if len(body) >= 12:
				ha = self.m_pmsManager.getHA(body[1])
				if None != ha:
					ha.resPerfomance(body[1],body[2:])
			return None
		elif body[0] == 'STAN':
			if len(body) >= REGION_MAX * AGE_MAX * GENDER_MAX + 3:
				ha = self.m_pmsManager.getHA(body[1])
				if None != ha:
					ha.resStatistic(body[2],body[3:])
			return None
		elif body[0] == 'STPCAN':
			if len(body) >= REGION_MAX * AGE_MAX * GENDER_MAX + 3:
				ha = self.m_pmsManager.getHA(body[1])
				if None != ha:
					ha.resStatisticPcb(int(body[2]),body[3:])		
			return None
			
		elif body[0] == 'ERR_DONT_HB' or body[0] == 'ERR_DONT_PF' or body[0] == 'ERR_DONT_ST' or body[0] == 'ERR_DONT_AN':
			if len(body) >= 2:
				ha = self.m_pmsManager.getHA(body[1])
				if None != ha:
					ha.m_status = body[0]
			return None
			
		elif body[0] == 'REGHA': #gsID,path,start,ip,port,config
			if len(body) >= 7:
				self.m_pmsManager.addHA(body[1],body[2],body[3],body[4],body[5],body[6])
				return 'OK|'
			else:
				return 'FAIL| param error'
		elif body[0] == 'LISTHA':
			return 'OK|%s' % (self.m_pmsManager.listHA())
		elif body[0] == 'PERFGS': #gsID
			if len(body) >= 2 :
				ha = self.m_pmsManager.getHA(body[1])
				if None != ha:
					return 'OK|%s' % (ha.printPerf())
				else:
					fileLog( '[response] Fail to PERFHA : Not found HA(GSID:%s)' % (body[1]))
					return 'FAIL|Not found HA(GSID:%s)' % (body[1])
			else:
				return 'FAIL| param error'
		elif body[0] == 'STATGS': #gsID
			if len(body) >= 2 :
				ha = self.m_pmsManager.getHA(body[1])
				if None != ha:
					return 'OK|%s' % (ha.printStat())
				else:
					fileLog( '[response] Fail to STATHA : Not found HA(GSID:%s)' % (body[1]))
					return 'FAIL|Not found HA(GSID:%s)' % (body[1])
			else:
				return 'FAIL| param error'						
		elif body[0] == 'STARTGS': #gsID
			if len(body) >= 2:
				ha = self.m_pmsManager.getHA(body[1])
				if None != ha:
					if True == ha.gsStart():
						return 'OK|%s start!' % (body[1])
					else:
						return 'FAIL|%s fail to start' % (body[1])
				else:
					return 'FAIL|GS[%s] is not found' % (body[1])		
		elif body[0] == 'STOPGS': #gsID
			if len(body) >= 2:
				ha = self.m_pmsManager.getHA(body[1])
				if None != ha:
					ha.gsStop()
					return 'OK|'
				else:
					return 'FAIL|GS[%s] is not found' % (body[1])
		elif body[0] == 'TESTGS': #gsID
			if len(body) >=2:
				ha = self.m_pmsManager.getHA(body[1])
				if None != ha:
					ha.gsTest()
				else:
					return 'FAIL|GS[%s] is not found' % (body[1])
		elif body[0] == 'NOTIGS': #gsID,msg
			if len(body) >=3:
				ha = self.m_pmsManager.getHA(body[1])
				if None != ha:
					ha.announce(body[2])
				else:
					return 'FAIL|GS[%s] is not found' % (body[1])
					
		else:
                        return 'FAIL|Not Support Msg'

class PMSServer(ThreadingTCPServer):
	allow_reuse_address = True
	
if __name__ == '__main__':
	server = PMSServer(('',PORT),RequestHandler)
	fileLog( '========= listening on Port %d' % (PORT))
#	server.handle_request()
	server.serve_forever()

